#!/bin/bash

include "objectv5.sh"
include "regex.sh"
include "test.sh"

ObjectV5::DefineClass PythonV2 "
    --string=cmdfifo
    --string=resfifo
    --string=pid
"

function PythonV2::PythonV2()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    PythonV2::cmdfifo "$this" "$(mktemp --dry-run "/tmp/python.cmd.XXXXXXXX")"
    PythonV2::resfifo "$this" "$(mktemp --dry-run "/tmp/python.res.XXXXXXXX")"
    PythonV2::pid "$this" "$BASHPID"
    return
}

function PythonV2::~PythonV2()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local cmdfifo="$(PythonV2::cmdfifo "$this")"
    local resfifo="$(PythonV2::resfifo "$this")"
    PythonV2::ExecNoResponse "$this" "exit(0)"
    rm -f "$cmdfifo" "$resfifo"
    return
}

function PythonV2::StartServer()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local cmdfifo="$(PythonV2::cmdfifo "$this")"
    local resfifo="$(PythonV2::resfifo "$this")"
    local pid="$(PythonV2::pid "$this")"
    if ! mkfifo -m 777 "$cmdfifo"; then
        loge "PythonV2 mkfifo failed"
        return 1
    fi

    if ! mkfifo -m 777 "$resfifo"; then
        loge "PythonV2 mkfifo failed"
        return 1
    fi

    if ! [[ "$pid" =~ (^${regex_dec_integer}$) ]]; then
        loge "PythonV2 default.pid不是数字"
        return 1
    fi

    if ! slient which python3; then
        loge "PythonV2 没有安装python3"
        return 1
    fi

    local server_code='
        import os as __os__
        import select as __select__

        try:
            __poller__ = __select__.poll()
            __poller__.register(__os__.open("'$cmdfifo'", __os__.O_RDONLY | __os__.O_NONBLOCK), __select__.POLLIN)

            def response(result):
                __responser__ = __os__.open("'$resfifo'", __os__.O_WRONLY)
                __os__.write(__responser__, (str(result) + "\n").encode(encoding="utf-8"))
                __os__.close(__responser__)

            __command__ = ""
            __linebreaker__ = 0
            while __os__.path.exists("/proc/'$pid'"):
                for __fd__, __flag__ in __poller__.poll(5000):
                    if not __flag__ & __select__.POLLIN:
                        continue

                    try:
                        while True:
                            __ch__ = __os__.read(__fd__, 1).decode(encoding="utf-8")
                            if __ch__ == "\n":
                                __linebreaker__ += 1
                                if __linebreaker__ == 3:
                                    break
                            else:
                                __linebreaker__ = 0
                            __command__ += __ch__
                    except BlockingIOError as __e__:
                        # response("read error:" + str(__e__))
                        continue
                    except Exception as __e__:
                        response("read error:" + str(__e__))

                    __linebreaker__ = 0

                    try:
                        exec(__command__)
                    except Exception as __e__:
                        response("run error:" + str(__e__))
                        response("cmd:" + __command__)

                    __command__ = ""

            __os__.remove("'$cmdfifo'")
            __os__.remove("'$resfifo'")
        except Exception as __e__:
            print("fatal: " + str(__e__))
    '
    python3 <<< "$(print_source 8 server_code)" &
    return
}

function PythonV2::Exec()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local cmdfifo="$(PythonV2::cmdfifo "$this")"
    local resfifo="$(PythonV2::resfifo "$this")"
    local content="$1"; shift
    if [ "$content" == "" ]; then
        loge "没有代码可以运行"
        return 1
    fi

    echo -en "${content}\n\n\n" > "$cmdfifo"
    result="$(cat "$resfifo")"
    if [[ "$result" =~ (error:) ]]; then
        echo -e "call: '\033[31m${content}\033[0m'" 1>&2
        echo -e "return: '\033[32m${result}\033[0m'" 1>&2
    fi
    echo "$result"
    return
}

function PythonV2::ExecNoResponse()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local cmdfifo="$(PythonV2::cmdfifo "$this")"
    local content="$1"; shift
    if [ "$content" == "" ]; then
        loge "没有代码可以运行"
        return 1
    fi

    echo -en "${content}\n\n\n" > "$cmdfifo"
    return
}

function PythonV2::Exec::Test1()
{
    local test_code1="
        response(1)
    "

    local test_code2="
        import yyyymmmmaaaallll
    "

    PythonV2 testpython
    test::should_return_true PythonV2::StartServer testpython
    test::run_cmd PythonV2::Exec testpython "$(print_source 8 test_code1)"
    test::should_be "1"

    test::run_cmd PythonV2::Exec testpython "$(print_source 8 test_code2)"
    test::should_be "error:No module named 'yyyymmmmaaaallll'"

    test::should_return_false PythonV2::Exec testpython ""
    return
}
test::register_test PythonV2::Exec::Test1
